package com.test.agent.service;

import cn.hutool.core.util.XmlUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.test.agent.Config;
import com.test.common.entity.auto.TestCaseDetailEntity;
import com.test.common.entity.auto.TestTaskCaseEntity;
import com.test.common.entity.auto.TestTaskEntity;
import com.test.common.enums.auto.TaskStatusEnum;
import com.test.common.repository.auto.TestCaseDetailRepository;
import com.test.common.repository.auto.TestTaskCaseRepository;
import com.test.common.repository.auto.TestTaskRepository;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javax.annotation.Resource;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Date;
import java.util.List;
import java.util.Optional;

@Slf4j
@Service
public class AgentService {

    public static void main(String[] args) {
    }

    /**
     * {
     *     "taskId":1,
     *     "testNG":[
     *         {
     *             "parallel":"classes",
     *             "classes":[
     *                 "script.DemoTest",
     *                 "script.DemoTest"
     *             ]
     *         },
     *         {
     *             "parallel":"false",
     *             "classes":[
     *                 "script.DemoTest",
     *                 "script.DemoTest"
     *             ]
     *         }
     *     ]
     * }
     */

    @Resource
    private Config config;

    @Resource
    private TestTaskRepository testTaskRepository;

    @Resource
    private TestTaskCaseRepository testTaskCaseRepository;

    @Resource
    private TestCaseDetailRepository testCaseDetailRepository;

    /**
     * 这个方法加上同步锁，只能单线程执行
     */
    @Scheduled(fixedDelay = 5000, initialDelay = 3000)
    public synchronized boolean scanner() {

        // 查询所有等待中的任务
        List<TestTaskEntity> testTaskEntityList = testTaskRepository.findAllByAutoAndStatus(true, TaskStatusEnum.WAITTING);

        // 任务分为：按照
        for (TestTaskEntity testTaskEntity : testTaskEntityList) {
            // todo 逐一判断是否到了任务设置的启动时间，先不做时间
            log.info("自动执行任务：{}", JSON.toJSONString(testTaskEntity));

            // 更新一波代码
            gitPull();

            // 把任务改成执行中
            testTaskEntity.setStatus(TaskStatusEnum.RUNNING);
            testTaskEntity.setStartTime(new Date());
            testTaskRepository.save(testTaskEntity);

            // 获取任务用例信息
            int taskId = testTaskEntity.getId();
            List<TestTaskCaseEntity> testTaskCaseEntityList = testTaskCaseRepository.findAllByTaskId(taskId);

            JSONObject paramJson = new JSONObject();
            paramJson.put("taskId", taskId);

            JSONArray testNGArray = new JSONArray();
            paramJson.put("testNG", testNGArray);

            // todo 暂时先做成全量串行的
            JSONObject detail = new JSONObject();
            detail.put("parallel", "none");
            testNGArray.add(detail);

            JSONArray clazzArray = new JSONArray();
            detail.put("classes", clazzArray);

            for (TestTaskCaseEntity taskCaseEntity : testTaskCaseEntityList) {
                Optional<TestCaseDetailEntity> caseDetailEntityOptional = testCaseDetailRepository.findByCaseNum(taskCaseEntity.getCaseNum());
                if (caseDetailEntityOptional.isPresent()) {
                    TestCaseDetailEntity testCaseDetailEntity = caseDetailEntityOptional.get();
                    if (testCaseDetailEntity.isAuto()) {
                        clazzArray.add(testCaseDetailEntity.getClazz());
                    }
                } else {
                    log.error("丢失用例信息：{}", taskCaseEntity.getCaseNum());
                }
            }

            if (clazzArray.size() > 0) {
                log.info("准备执行TestNG：{}", paramJson.toJSONString());
                File taskFolder = new File(config.getScriptLocation() + File.separator + "testNG");
                if (!taskFolder.exists()) {
                    log.info("初始化创建testNG.xml目录：{}", taskFolder.mkdir());
                }

                JSONArray testNG = paramJson.getJSONArray("testNG");

                Document document = XmlUtil.createXml("suite");

                Element suite = document.getDocumentElement();
                suite.setAttribute("name", "TaskId:" + taskId);
                suite.setAttribute("verbose", "1");

                for (int i = 0; i < testNG.size(); i++) {
                    JSONObject object = testNG.getJSONObject(i);

                    Element test = document.createElement("test");
                    test.setAttribute("name", "TestName_" + i);
                    test.setAttribute("parallel", object.getString("parallel"));
                    test.setAttribute("thread-count", "8");
                    suite.appendChild(test);

                    Element classes = document.createElement("classes");
                    test.appendChild(classes);

                    JSONArray classArray = object.getJSONArray("classes");
                    for (int j = 0; j < classArray.size(); j++) {
                        String clazz = classArray.getString(j);
                        Element aClass = document.createElement("class");
                        aClass.setAttribute("name", clazz);
                        classes.appendChild(aClass);
                    }
                }

                log.info("taskId：{}，testNG.xml：{}", taskId, XmlUtil.toStr(document, false));
                XmlUtil.toFile(document, taskFolder.getAbsolutePath() + File.separator + taskId + ".xml", "UTF-8");

                String[] command;
                String charsetName;
                if (System.getProperty("os.name").toLowerCase().contains("linux")) {
                    log.info("执行机为Linux系统");
                    command = new String[] { "/bin/sh", "-c", config.getScriptLocation() + File.separator + "run.sh " + taskId + ".xml" };
                    charsetName = "UTF-8";
                } else if (System.getProperty("os.name").toLowerCase().contains("windows")) {
                    log.info("执行机为Windows系统");
                    command = new String[] { "cmd", "/c", config.getScriptLocation() + File.separator + "run.bat " + taskId + ".xml" };
                    charsetName = "GBK";
                } else {
                    log.error("不支持的操作系统：{}", System.getProperty("os.name").toLowerCase());
                    continue;
                }

                try {
                    Process process = Runtime.getRuntime().exec(command);

                    Thread thread1 = new Thread(() -> {
                        try {
                            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), charsetName));
                            String line;
                            while ((line = reader.readLine()) != null) {
                                log.info(line);
                            }
                            reader.close();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        log.info("InputStream Reader OVER");
                    });
                    thread1.setName("task-" + taskId + "-reader");
                    thread1.start();

                    Thread thread2 = new Thread(() -> {
                        try {
                            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream(), charsetName));
                            String line;
                            while ((line = reader.readLine()) != null) {
                                log.info(line);
                            }
                            reader.close();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        log.info("ErrorStream Reader OVER");
                    });
                    thread2.setName("task-" + taskId + "-error-reader");
                    thread2.start();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return true;
    }

    public void gitClone() {
        File file = new File(config.getScriptLocation());
        log.info("仓库保存路径：{}", file.getAbsolutePath());
        if (file.exists()) {
            delete(file);
        }

        try {
            Git git = Git.cloneRepository()
                    .setCredentialsProvider(new UsernamePasswordCredentialsProvider(config.getScriptUserName(), config.getScriptPassWord()))
                    .setURI(config.getScriptUrl())
                    .setDirectory(file)
                    .call();
            // 切换分支
            // git.checkout()
            //         .setCreateBranch(true)
            //         .setName("master")
            //         .call();
            // git.push().setPushAll()
            //         .setCredentialsProvider(new UsernamePasswordCredentialsProvider(config.getScriptUserName(), config.getScriptPassWord()))
            //         .call();
            git.close();
            log.info("克隆成功");
        } catch (Exception e) {
            log.error("克隆自动化脚本失败");
            e.printStackTrace();
        }
    }

    public boolean gitPull() {
        try {
            File file = new File(config.getScriptLocation());
            Git git = Git.open(file);
            git.pull()
                    .setCredentialsProvider(new UsernamePasswordCredentialsProvider(config.getScriptUserName(), config.getScriptPassWord()))
                    .call();
            git.close();
            log.info("更新自动化脚本成功");
        } catch (Exception e) {
            log.error("更新自动化脚本失败");
            e.printStackTrace();
            return false;
        }
        return true;
    }

    private void delete(File folder) {
        File[] files = folder.listFiles();
        if (null != files) {
            for (File file : files) {
                if (file.isDirectory())
                    delete(file);
                else if (file.isFile()) {
                    log.info("删除：{}，成功：{}", file.getAbsolutePath(), file.delete());
                }
            }
            log.info("删除：{}，成功：{}", folder.getAbsolutePath(), folder.delete());
        }
    }
}
